extern crate rustc_serialize;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use rustc_serialize::json::{self, DecodeResult};

pub fn hello() {
    println!("hello, gitosc webhook");
}

// type Hook
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Repo {
    pub url: String,
}

#[derive(Debug)]
pub struct Data {
    pub reference: String,
    pub repository: Repo,
}

impl Encodable for Data {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_struct("Data", 2, |s| {
            try!(s.emit_struct_field("ref", 0, |s| self.reference.encode(s)));
            try!(s.emit_struct_field("repository", 1, |s| self.repository.encode(s)));
            Ok(())
        })
    }
}

impl Decodable for Data {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_struct("Data", 2, |d| {
            Ok(Data{
                reference: try!(d.read_struct_field("ref", 0, |d| Decodable::decode(d))),
                repository: try!(d.read_struct_field("repository", 1, |d| Decodable::decode(d))),
            })
        })
    }
}

#[derive(Debug)]
pub struct Hook {
    pub password: String,
    pub pushdata: Data,
}

impl Encodable for Hook {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_struct("Hook", 2, |s| {
            try!(s.emit_struct_field("password", 0, |s| self.password.encode(s)));
            try!(s.emit_struct_field("push_data", 1, |s| self.pushdata.encode(s)));
            Ok(())
        })
    }
}

impl Decodable for Hook {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_struct("Hook", 2, |d| {
            Ok(Hook{
                password: try!(d.read_struct_field("password", 0, |d| Decodable::decode(d))),
                pushdata: try!(d.read_struct_field("push_data", 1, |d| Decodable::decode(d))),
            })
        })
    }
}

impl Hook {
    pub fn new(raw: &str) -> DecodeResult<Hook> {
        json::decode(raw)
    }
}

#[cfg(test)]
mod hooktest {
    extern crate rustc_serialize;
    use rustc_serialize::json;

    use super::Hook;

    #[test]
    fn show_type() {
        let raw_json = r#"{"password":"123456","push_data":{"ref":"reference","repository":{"url":"http://git.oschina.net","ddd":12}}}"#;
        let result = Hook::new(raw_json);
        match result {
            Ok(ref x) => println!("Hook: {}", json::encode(x).unwrap()),
            Err(e) => println!("{:?}", e),
        }
    }
}
